/* udp-broadcast-server.c:
 * udp broadcast server example
 * Example Stock Index Broadcast:
 */
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <errno.h>
#include <string.h>
#include <time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string>
#include <vector>
#include <iostream>
#include <map>
#include "ConInf.h"

using namespace std;
#ifndef TRUE
#define TRUE 1
#define FALSE 0
#endif

extern int mkaddr(void *addr, int *addrlen, char *str_addr, char *protocol);

#define MAXQ 4

/*
 * Randomly change one index quotation:
 */
/*
 * This function reports the error and
 * exits back to the shell:
 */
void getFile(string getPath, string savePath, string destIp, string user) {
	if (getPath == "")
		throw "nothing to take";
	FILE *fp;
	string task;
	task.append("scp "). append(user). append("@"). append(destIp). append(":"). append(
			getPath).append(" "). append(savePath);
	//printf(task.c_str());
	fp = popen(task.c_str(), "w");

}

static void displayError(const char *on_what) {
	fputs(strerror(errno), stderr);
	fputs(": ", stderr);
	fputs(on_what, stderr);
	fputc('\n', stderr);
	exit(1);
}

int saySmth(char* svaddr, char* destaddr, bool bc, string bcbuf) {

	char *bp;/* Buffer and ptr */
	int z; /* Status return code */
	int s; /* Socket */
	struct sockaddr_in adr_srvr;/* AF_INET */
	int len_srvr; /* length */
	struct sockaddr_in adr_bc; /* AF_INET */
	int len_bc; /* length */
	static int so_broadcast = TRUE;
	static int so_reuseaddr = TRUE;

	len_srvr = sizeof adr_srvr;

	z = mkaddr(&adr_srvr, /* Returned address */
	&len_srvr, /* Returned length */
	svaddr, /* Input string addr */
	"udp"); /* UDP protocol */

	if (z == -1)
		displayError("Bad server address");

	/*
	 * Form the broadcast address:
	 */
	len_bc = sizeof adr_bc;

	z = mkaddr(&adr_bc, /* Returned address */
	&len_bc, /* Returned length */
	destaddr, /* Input string addr */
	"udp"); /* UDP protocol */

	if (z == -1)
		displayError("Bad broadcast address");

	/*
	 * Create a UDP socket to use:
	 */
	s = socket(AF_INET, SOCK_DGRAM, 0);
	if (s == -1)
		displayError("socket()");

	if (bc) {
		/*
		 * Allow broadcasts:
		 */
		z = setsockopt(s, SOL_SOCKET, SO_BROADCAST, &so_broadcast,
				sizeof so_broadcast);

		if (z == -1)
			displayError("setsockopt(SO_BROADCAST)");

		z = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &so_reuseaddr,
				sizeof so_reuseaddr);

		/*
		 * Bind an address to our socket, so that
		 * client programs can listen to this
		 * server:
		 */

	} else {
		so_broadcast = FALSE;
		z = setsockopt(s, SOL_SOCKET, SO_BROADCAST, &so_broadcast, 0);

		if (z == -1)
			displayError("setsockopt(SO_BROADCAST)");

	}
	z = bind(s, (struct sockaddr *) &adr_srvr, len_srvr);

	if (z == -1)
		displayError("bind()");

	/*
	 * Broadcast the updated info:
	 */

	bcbuf.append(" ").append(svaddr);
	cout << bcbuf;
	char* tmp = strdup(bcbuf.c_str());

	z = sendto(s, tmp, 1024, 0, (struct sockaddr *) &adr_bc, len_bc);

	free(tmp);
	if (z == -1)
		displayError("sendto()");

	return 1;
}

void splitString(string str, char delim, vector<string> &splited) {
	int j = 0;
	for (int i = 0; i < str.length(); ++i)
		if (str[i] == ' ' || i == str.length() - 1) {

			splited.push_back(str.substr(j, i));
			j = i + 1;

		}
}

void sayGetFileList(char *dstAddr, char *svAddr, bool everyone) {
	char* bcbuf = "GiveMeList";
	saySmth(svAddr, dstAddr, everyone, bcbuf);
}

void sayTakeFileList(char *dstAddr, char *svAddr) {
	char* bcbuf = "Take ~/share/.lists/";
	saySmth(svAddr, dstAddr, false, bcbuf);
}

sockaddr_in handleIncomingUdpConnection(char* bc_addr, char* bcbuf) {
	//char bcbuf[1024];
	int z;
	int x;
	struct sockaddr_in adr;
	struct sockaddr_in inaddr;/* AF_INET */
	int len_inet; /* length */
	int s; /* Socket */
	char dgram[512];

	static int so_reuseaddr = TRUE;
	//static char *bc_addr = "255.255.255.255:9097";

	s = socket(AF_INET, SOCK_DGRAM, 0);
	if (s == -1)
		displayError("socket()");

	/*
	 * Form the broadcast address:
	 */
	len_inet = sizeof adr;

	z = mkaddr(&adr, &len_inet, bc_addr, "udp");

	if (z == -1)
		displayError("Bad broadcast address");

	/*
	 * Allow multiple listeners on the
	 * broadcast address:
	 */
	z = setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &so_reuseaddr,
			sizeof so_reuseaddr);

	if (z == -1)
		displayError("setsockopt(SO_REUSEADDR)");

	/*
	 * Bind our socket to the broadcast address:
	 */
	z = bind(s, (struct sockaddr *) &adr, len_inet);

	if (z == -1)
		displayError("bind(2)");

	/*
	 * Wait for a broadcast message:
	 */
	memset(&inaddr, 0, sizeof inaddr);
	int len = sizeof(struct sockaddr_in);
	z = recvfrom(s, /* Socket */
	bcbuf, /* Receiving buffer */
	1024,/* Max rcv buf size */
	0, /* Flags: no options */
	(struct sockaddr*) &inaddr, /* Addr */
	(socklen_t*) &len); /* Addr len, in & out */

	//   printf("My address: %s\n", inet_ntoa(inaddr.sin_addr));
	//  struct in_addr ia;
	// printf("host id %u\n", gethostid());
	// ia.s_addr = htonl(gethostid());
	//   printf("My address: %s\n", inet_ntoa(ia));
	// if(inaddr.sin_addr.s_addr == ia.s_addr){
	// printf("me!!! =)");

	//}
	if (z < 0)
		displayError("recvfrom(2)"); /* else err */

	//    printf(bcbuf);


	return inaddr;
}

void action(string incoming, char* destIp, char* servAddr) {
	vector<string> splited;
	splitString(incoming, ' ', splited);
	string savepath = "~/scpTransfer/";
	if (splited[0] == "Take") {
		try {
			getFile(splited[1].append(destIp), savepath, destIp, "twin");
		} catch (string mes) {
			cout << mes;
		}
	} else if (splited[0] == "GiveMeList") {
		sayTakeFileList(destIp, servAddr);
	} else if (splited[0] == "Updated") {
		//sayTakeFileList(destIp,servAddr);
	} else {
		printf("Unknown request");
	}

}

int main(int argc, char **argv) {

	map<string, ConInf> SharComputers;

	short x; /* index of Stock Indexes */
	double I0; /* Initial index value */
	double I; /* Index value */
	string bcbuf[1024] = "hi ~/share/.lists/", *bp;/* Buffer and ptr */
	int z; /* Status return code */
	int s; /* Socket */
	struct sockaddr_in adr_srvr;/* AF_INET */
	int len_srvr; /* length */
	struct sockaddr_in adr_bc; /* AF_INET */
	int len_bc; /* length */
	static int so_broadcast = TRUE;
	static char *sv_addr = "127.0.0.1:9097", *bc_addr = "255.255.255.255:9097";

	/*
	 * Form a server address:
	 */
	if (argc > 2)
		/* Server address: */
		sv_addr = argv[2];

	if (argc > 1)
		/* Broadcast address: */
		bc_addr = argv[1];

	/*
	 * Form the server address:
	 */
	//printf("%d",sayHello(sv_addr,bc_addr,true,""));
	// printf("%d",sayHello("192.168.1.4:9097","192.168.1.2:9097",false,""));
	string str = "sas gfg";
	string token;
	vector<string> splited;

	//sayGetFileList("192.168.1.255","192.168.1.4",true);


	char message[1024];
	struct sockaddr_in incAdr;
	int fd = fork();
	if (fd == 0) {
		sleep(1);
		sayGetFileList(bc_addr, sv_addr, true);
	} else {

		incAdr = handleIncomingUdpConnection(bc_addr, message);
		ConInf* connection = new ConInf(inet_ntoa(incAdr.sin_addr));

		splitString(message, ' ', splited);
		cout << splited[0];
		//	cout<<incA;
	}
	/*	         if(path.substr(0,2)=="hi"){
	 path=path.substr(3,sizeof(bcbuf));
	 //printf(path.c_str());
	 getFile(path,"~/scpTransfer/",inet_ntoa(inaddr.sin_addr),"twin");
	 printf("%s - %s",inet_ntoa(inaddr.sin_addr),path);
	 }
	 */
	//token=strtok(str," ");
	//printf("nasdf");


	return 0;
}

